package app.repositories.impl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

import app.models.Consulta;
import app.models.Paciente;
import app.models.enums.StatusConsulta;
import app.repositories.ConsultaRepository;
import app.repositories.Repository;
import app.util.DateUtils;
import br.com.caelum.vraptor.ioc.Component;

@Component
public class ConsultaRepositoryImpl
    extends Repository<Consulta, Long>
    implements ConsultaRepository {
	
	private final String DATA = "data";
	private final String DATA_INICIAL = "dataInicial";
	private final String DATA_FINAL = "dataFinal";
	private final String TIPO = "tipo";
	private final String STATUS = "status";
	private final String HORA_INICIAL = "horaInicial";
	private final String HORA_FINAL = "horaFinal";
	private final String ID_PACIENTE = "idPaciente";
	private final String ID_ESPECIALISTA = "idEspecialista";
	private final String ID_ESPECIALIDADE = "idEspecialidade";

	
	public ConsultaRepositoryImpl(EntityManager entityManager) {
		super(entityManager);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Consulta> findAllByFilter(String filter) {
		Criteria criteria = createCriteria();
		
		Calendar calIni = Calendar.getInstance();
		Calendar calFim = Calendar.getInstance();
		
		// Todas as consultas referente ao ano do "filter"
		if (filter.length() == 4) {
			calIni.set(Integer.valueOf(filter), 0, 1);
			calFim.set(Integer.valueOf(filter), 11, 31);
			criteria.add(Restrictions.between(DATA, calIni, calFim));
			
		// Todas as consultas referente ao dia do "filter"
		} else if (filter.length() <= 2) {
			calIni.set(Calendar.YEAR, Calendar.MONTH, Integer.valueOf(filter));
			criteria.add(Restrictions.eq(DATA, calIni));
		}
		
		criteria.addOrder(Order.asc(DATA));
		
		return criteria.list();
	}

	@Override
	public String findFirstYear() {
		StringBuilder stringBuilder = new StringBuilder();
		
		stringBuilder.append("select min(data) from consulta");
		
		Query query = entityManager.createQuery(stringBuilder.toString());
		
		return query.getSingleResult().toString();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Consulta> findConsultasByDate(Integer day, Integer month, Integer year) {
		
		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
		
		Criteria criteria = createCriteria();
		Calendar cal = Calendar.getInstance();
		
		cal.set(year, month - 1, day);
		
		criteria.add(Restrictions.eq(DATA, formatter.format(cal.getTime())));
		criteria.addOrder(Order.asc(DATA));
		
		return criteria.list();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Consulta> findConsultasByFilters(Consulta consulta) {
		
		Criteria criteria = createCriteria();
		criteria.setFetchMode("especialista", FetchMode.JOIN);
		criteria.setFetchMode("especialidade", FetchMode.JOIN);
		criteria.setFetchMode("subEspecialidade", FetchMode.JOIN);
		criteria.setFetchMode("secretaria", FetchMode.JOIN);
		criteria.setFetchMode("paciente", FetchMode.JOIN);
		
		if (null != consulta.getIdEspecialista()) {
			criteria.add(Restrictions.eq(ID_ESPECIALISTA, consulta.getIdEspecialista()));
		}
		
		if (null != consulta.getTipo()) {
			criteria.add(Restrictions.eq(TIPO, consulta.getTipo()));
		}
		
		if (null != consulta.getIdEspecialidade()) {
			criteria.add(Restrictions.eq(ID_ESPECIALIDADE, consulta.getIdEspecialidade()));
		}
		
		if (null != consulta.getData()) {
			criteria.add(Restrictions.eq(DATA, consulta.getData()));
		}
		
		if (null != consulta.getDataInicial() && null != consulta.getDataFinal() && !consulta.getDataInicial().isEmpty() && !consulta.getDataFinal().isEmpty() ) {
			criteria.add(Restrictions.ge(DATA, consulta.getDataInicial()));
			criteria.add(Restrictions.le(DATA, consulta.getDataFinal()));
		}
		
		criteria.addOrder(Order.desc("data")).addOrder(Order.asc("horaInicial")).addOrder(Order.asc("horaFinal"));
		
		return criteria.list();
	}

	
	// Últimas consultas com StatusConsulta = {FINALIZADA, REALIZADA}
	@SuppressWarnings("unchecked")
	@Override
	public List<Consulta> findLastConsultas() {
		List<StatusConsulta> statusList = new ArrayList<StatusConsulta>();
		statusList.add(StatusConsulta.FINALIZADA);
		statusList.add(StatusConsulta.REALIZADA);
		
		Criteria criteria = createCriteria();
		
	    criteria.add(Restrictions.le(DATA, DateUtils.getDataAtual()));
	    criteria.add(Restrictions.le(HORA_INICIAL, DateUtils.getHoraCompletaAtual()));
	    criteria.add(Restrictions.in(STATUS, statusList));
	    
	    criteria.addOrder(Order.desc(DATA));
	    criteria.setMaxResults(5);
		
		return criteria.list();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Consulta> findNextConsultas() {
		List<StatusConsulta> statusList = new ArrayList<StatusConsulta>();
		statusList.add(StatusConsulta.CANCELADA);
		
		Criteria criteria = createCriteria();
		
	    criteria.add(Restrictions.ge(DATA, DateUtils.getDataAtual()));
	    criteria.add(Restrictions.ge(HORA_INICIAL, DateUtils.getHoraCompletaAtual()));
	    criteria.add(Restrictions.in(STATUS, statusList));
	    criteria.setMaxResults(5);
		
		return criteria.list();
	}

	@Override
	public Integer findConsultasInDayOnMonth(Consulta consulta, String data) {
		StringBuilder stringBuilder = new StringBuilder();
		
		stringBuilder.append(String.format("select count(*) from Consulta c where c.data = '%s'", data));
		
		if (null != consulta) {
			if (null != consulta.getTipo()) {
				stringBuilder.append(String.format(" and c.tipo = '%s'", consulta.getTipo()));
			}
			
			if (null != consulta.getIdEspecialista()) {
				stringBuilder.append(String.format(" and c.idEspecialista = '%s'", consulta.getIdEspecialista()));
			}
			
			if (null != consulta.getIdEspecialidade()) {
				stringBuilder.append(String.format(" and c.idEspecialidade = '%s'", consulta.getIdEspecialidade()));
			} else if (null != consulta.getIdSubEspecialidade()) {
				stringBuilder.append(String.format(" and c.idSubEspecialidade = '%s'", consulta.getIdSubEspecialidade()));
			}
			
			if (null != consulta.getIdPaciente()) {
				stringBuilder.append(String.format(" and c.idPaciente = '%s'", consulta.getIdPaciente()));
			}
			
			if (null != consulta.getStatus()) {
				stringBuilder.append(String.format(" and c.status = '%s'", consulta.getStatus()));
			}
		}
		
		Query query = entityManager.createQuery(stringBuilder.toString());
		
		return Integer.valueOf(query.getSingleResult().toString());
	}
	
	public Paciente findPacienteByConsultaId(Long id){
		Consulta consulta = this.find(id);
		return consulta.getPaciente();
	}
	
	public Consulta findConsultaByPacienteIdAndByTimeAndByDate(Consulta consulta){
		Criteria criteria = createCriteria();
		
		criteria.add(Restrictions.eq(ID_PACIENTE, consulta.getIdPaciente()));
		criteria.add(Restrictions.eq(DATA, consulta.getData()));
		criteria.add(Restrictions.eq(HORA_INICIAL, consulta.getHoraInicial()));
		criteria.add(Restrictions.eq(HORA_FINAL, consulta.getHoraFinal()));
		criteria.add(Restrictions.ne(STATUS, StatusConsulta.CANCELADA));
		
		return (Consulta) criteria.uniqueResult();
	}
	
	public Consulta findConsultaByEspecialistaIdAndByTimeAndByDate(Consulta consulta){
		Criteria criteria = createCriteria();
		
		criteria.add(Restrictions.eq(ID_ESPECIALISTA, consulta.getIdEspecialista()));
		criteria.add(Restrictions.eq(DATA, consulta.getData()));
		criteria.add(Restrictions.eq(HORA_INICIAL, consulta.getHoraInicial()));
		criteria.add(Restrictions.eq(HORA_FINAL, consulta.getHoraFinal()));
		criteria.add(Restrictions.ne(STATUS, StatusConsulta.CANCELADA));
		
		return (Consulta) criteria.uniqueResult();
	}
	
	@SuppressWarnings("unchecked")
	public List<Consulta> findLatePaymentsByPacienteId(Integer pacienteId){
		Criteria criteria = createCriteria();
		
		criteria.add(Restrictions.eq(ID_PACIENTE, pacienteId));
		criteria.add(Restrictions.eq(STATUS, StatusConsulta.REALIZADA));
		
		return criteria.list();
	}

	@Override
	public void pay(Consulta consulta, String dataPagamento) {
		StringBuilder stringBuilder = new StringBuilder();
		stringBuilder.append("update Consulta set dataPagamento = '" + dataPagamento + "' ");
		
		if (consulta.getStatus() == StatusConsulta.MARCADA) {
			stringBuilder.append(" , status = '" + StatusConsulta.PAGA + "' ");
		} else if (consulta.getStatus() == StatusConsulta.REALIZADA) {
			stringBuilder.append(" , status = '" + StatusConsulta.FINALIZADA + "' ");
		} else if (consulta.getStatus() == StatusConsulta.PENDENTE) {
			stringBuilder.append(" , status = '" + StatusConsulta.ATRASADA + "' ");
		}
		
		stringBuilder.append("where id = " + consulta.getId() + "");
		
		Query query = entityManager.createQuery(stringBuilder.toString());
		query.executeUpdate();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Consulta> consultasBetweenDates(String dataInicio, String dataFim) {
		Criteria criteria = createCriteria();
		
		criteria.add(Restrictions.between(DATA, dataInicio, dataFim));
		
		return (List<Consulta>) criteria.list();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Consulta> consultasBetweenDatesAndFilter(Consulta consulta, String dataInicio, String dataFim) {
		Criteria criteria = createCriteria();
		
		criteria.add(Restrictions.between(DATA, dataInicio, dataFim));
		
		if (null != consulta) {
			if (null != consulta.getStatus()) {
				criteria.add(Restrictions.eq(STATUS, consulta.getStatus()));
			}
			
			if (null != consulta.getTipo()) {
				criteria.add(Restrictions.eq(TIPO, consulta.getTipo()));
			}
		}
		
		return (List<Consulta>) criteria.list();
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<Consulta> consultasBetweenDatesAndStatus(String dataInicial, String dataFinal, List<StatusConsulta> statusConsulta) {
			Criteria criteria = createCriteria();
		
		criteria.add(Restrictions.between(DATA, dataInicial, dataFinal));
		criteria.add(Restrictions.in(STATUS, statusConsulta));
		
		return (List<Consulta>) criteria.list();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Consulta> findAllForTask() {
		Criteria criteria = createCriteria();
		
		criteria.add(Restrictions.ne(STATUS, StatusConsulta.CANCELADA));
		criteria.add(Restrictions.ne(STATUS, StatusConsulta.FINALIZADA));
		criteria.add(Restrictions.ne(STATUS, StatusConsulta.PENDENTE));
		criteria.add(Restrictions.ne(STATUS, StatusConsulta.ATRASADA));
		criteria.add(Restrictions.le(DATA, DateUtils.getDataAtual()));
		
		return (List<Consulta>) criteria.list();
	}

}
